package ace.system;

import java.io.IOException;
import java.util.Vector;
import javax.microedition.lcdui.Image;

import javax.microedition.lcdui.game.GameCanvas;

import ace.Main;
import ace.game.Sprite;
/*import towerz.game.entity.hut.HutFactory;
import towerz.game.entity.tower.TowerFactory;*/
import ace.states.GameState;
import ace.states.StateFactory;
import ace.system.sound.SFXPlayer;
import ace.system.sound.SimplePlayer;
import ace.system.sound.SimpleTones;

public final class StateManager extends GameCanvas implements Runnable {

	/**
	 * Protected constructor
	 * @param suppressKeyEvents
	 */
	protected StateManager(boolean suppressKeyEvents) {
		super(suppressKeyEvents);

		setFullScreenMode(true);
		
		//Clear input
		m_TimerUp		= 0;
		m_TimerDown		= 0;
		m_TimerLeft		= 0;
		m_TimerRight	= 0;
		m_HoldRight		= false;
		m_HoldLeft		= false;
		m_HoldFire		= false;
		m_HoldDown		= false;
		m_HoldUp		= false;

		//Initialize data
		m_Running		= true;
		m_RemovalDepth	= 0;
		m_isSoundOff	= false;
		m_paused		= false;
		
		//No image
		m_Dark			= null;
		m_Background	= null;
		m_Developer		= null;
		
		//Create state stack
		m_StateList = new Vector();
		
		//Calculate column
		m_Column	= (getWidth()  + Utilities.TILE_WIDTH  - 1) / Utilities.TILE_WIDTH;
		m_Row		= (getHeight() + Utilities.TILE_HEIGHT - 1) / Utilities.TILE_HEIGHT;

		//Audio MIGHT CAUSED HORRIBLE LAG
		m_SFXPlayers	= new SFXPlayer();
		m_SimplePlayer	= new SimplePlayer();
		m_SimpleTones	= new SimpleTones();
		m_isSoundOff	= true; //Defaulted to muted

		//Create factories
		/*HutFactory.instance();
		TowerFactory.instance();*/
	}
	
	public synchronized static StateManager instance() {
		//Create state manager if doesn't exist
		if (s_Instance == null) s_Instance = new StateManager(false);
		return s_Instance;
	}
	
	public void loadImages() {
		//Skip if not null
		if (m_Background != null) return;
		
		//Load images
		try {
			m_Developer		= Image.createImage(Utilities.INTERFACE_FOLDER + "/developer.png");
			m_Background	= Image.createImage(Utilities.BACKGROUND_FOLDER + "/background.png");
		} catch (IOException ex) {}
		
		//Create dark image
		int[] Colors = new int[getWidth() * getHeight()];
		for (int i = 0; i < Colors.length; i++) Colors[i] = 0x80000000;
		m_Dark = Image.createRGBImage(Colors, getWidth(), getHeight(), true);
	}
	
	public Image getDarkImage()			{	return m_Dark;			}
	public Image getDeveloperImage()	{	return m_Developer;		}
	public Image getBackgroundImage()	{	return m_Background;	}

	protected void hideNotify() {
		super.hideNotify();

		if(!m_isSoundOff){
			m_SimplePlayer.setMute(true);
			m_SimpleTones.setMute(true);
			m_SFXPlayers.setMute(true);
		}
		m_paused = true;

		((GameState)m_StateList.elementAt(m_StateList.size()-1)).onPause();
	}

	protected void showNotify() {
		super.showNotify();

		if(!m_isSoundOff){
			m_SimplePlayer.setMute(false);
			m_SimpleTones.setMute(false);
			m_SFXPlayers.setMute(false);
		}
		m_paused = false;

		((GameState)m_StateList.elementAt(m_StateList.size()-1)).onResume();
	}

	public boolean isSoundOff(){
		return m_isSoundOff;
	}

	public void setSoundOff(boolean isSoundOff){
		m_isSoundOff = isSoundOff;
		m_SimplePlayer.setMute(isSoundOff);
		m_SimpleTones.setMute(isSoundOff);
		m_SFXPlayers.setMute(isSoundOff);
	}

    public int getHeight(){
        return super.getHeight() - 0;
    }
    
	public int getRow() {
		return m_Row;
	}

	public int getColumn() {
		return m_Column;
	}

	public SimplePlayer getSimplePlayer(){
		return m_SimplePlayer;
	}

	public SimpleTones getSimpleTones(){
		return m_SimpleTones;
	}

	public SFXPlayer getSFXPlayer(){
		return m_SFXPlayers;
	}

	public void goTo(int id, Vector parameters, boolean swap) {
		//Ensure states exist
		if (m_StateList != null) {
			//Prepare variables
			int i			= 0;
			boolean Exist	= false;

			//Find state
			while (!Exist && i < m_StateList.size()) {
				//Is it exist?
				if (m_StateList.elementAt(i) != null)
					if (((GameState)m_StateList.elementAt(i)).getID() == id) Exist = true;

				//Next
				i++;
			}
			
			//If exist, return, otherwise, add a new one
			if (Exist) returnTo(id, parameters);
			else {
				//Create new state
				GameState NewState = StateFactory.createState(id, parameters);
				if (NewState == null) return;

				//If not swapped, add state
				if (!swap) addState(NewState);
				else {
					//Remove top
					m_RemovalDepth++;

					//Add the state before current state
					addState(NewState);
					m_StateList.removeElement(m_StateList.lastElement());
					m_StateList.insertElementAt(NewState, m_StateList.size() - 1);
				}
			}
		}
	}

	private void addState(GameState state) {
		//Skip if null
		if (state == null) return;
		
		//If current state exist, exit it
		if (!m_StateList.isEmpty())
			if (m_StateList.lastElement() != null) ((GameState)m_StateList.lastElement()).onExit();
		
		//Add state
		m_StateList.addElement(state);

		//Initialize
		state.initialize();
		state.onEnter();
	}

	private void removeState() {
		//Ensure state list exist
		if (m_StateList == null)				return;
		if (m_StateList.isEmpty())				return;
		if (m_StateList.lastElement() == null)	return;

		//Remove state
		((GameState)m_StateList.lastElement()).onExit();
		((GameState)m_StateList.lastElement()).onRemove();
		m_StateList.removeElement(m_StateList.lastElement());

		//Enter state below
		if (!m_StateList.isEmpty())
			if (m_StateList.lastElement() != null) ((GameState)m_StateList.lastElement()).onEnter();
	}

	private void returnTo(int id, Vector parameters) {
		//Initialize variable
		m_RemovalDepth	= 0;
		boolean Found	= false;
		int i			= m_StateList.size() - 1;

		//While exist
		while (i >= 0 & !Found) {
			//Remove null, if not
			if (m_StateList.elementAt(i) != null) {
				//If the same id
				if (((GameState)m_StateList.elementAt(i)).getID() == id) {
					//Found
					Found = true;
					((GameState)m_StateList.elementAt(i)).onEnter();
				} else m_RemovalDepth++;
			} else m_RemovalDepth++;

			//Next
			i--;
		}
	}

	protected void checkKey(long time, int keys) {
		//Manage timer
		if (m_TimerUp > 0)						m_TimerUp -= time;
		if (m_TimerUp <= 0 && m_HoldUp)			m_HoldUp = false;
		if (m_TimerDown > 0)					m_TimerDown -= time;
		if (m_TimerDown <= 0 && m_HoldDown)		m_HoldDown = false;
		if (m_TimerLeft > 0)					m_TimerLeft -= time;
		if (m_TimerLeft <= 0 && m_HoldLeft)		m_HoldLeft = false;
		if (m_TimerRight > 0)					m_TimerRight -= time;
		if (m_TimerRight <= 0 && m_HoldRight)	m_HoldRight = false;

		//if Up is pressed
		if ((keys & GameCanvas.UP_PRESSED) != 0) {
			//Is holding
			m_HoldUp = true;
			m_TimerUp = Utilities.GAME_BUTTON_WAIT;

			//Free up button
			m_HoldDown = false;
			m_TimerDown = 0;
		}

		//if down is pressed
		if ((keys & GameCanvas.DOWN_PRESSED) != 0) {
			//Is holding
			m_HoldDown = true;
			m_TimerDown = Utilities.GAME_BUTTON_WAIT;

			//Free up button
			m_HoldUp = false;
			m_TimerUp = 0;
		}

		//if left is pressed
		if ((keys & GameCanvas.LEFT_PRESSED) != 0) {
			//Is holding
			m_HoldLeft	= true;
			m_TimerLeft = Utilities.GAME_BUTTON_WAIT;

			//Free right button
			m_HoldRight		= false;
			m_TimerRight	= 0;
		}

		//if right is pressed
		if ((keys & GameCanvas.RIGHT_PRESSED) != 0) {
			//Is holding
			m_HoldRight	= true;
			m_TimerRight = Utilities.GAME_BUTTON_WAIT;

			//Free right button
			m_HoldLeft	= false;
			m_TimerLeft	= 0;
		}

		//If fire key is pressed
		if ((keys & FIRE_PRESSED) != 0) m_HoldFire = true;
	}

	public void keyPressed(int keyCode) {
		//Super
		super.keyPressed(keyCode);

		//Send to last state
		if (m_StateList != null)
			if (!m_StateList.isEmpty())
				if (m_StateList.lastElement() != null)
					((GameState)m_StateList.lastElement()).keyPressed(keyCode);
	}

	public void run() {
		//Get current time
		long Current 	= System.currentTimeMillis();
		long Difference	= 0;
		long Old		= 0;
		
		//While still running
		while (m_Running) {
			//Sleep if difference less than frame time
			Difference = System.currentTimeMillis() - Current;
			if (Difference < (1000 / Utilities.GAME_FPS)) {
				try {
					Thread.sleep((1000 / Utilities.GAME_FPS) - Difference);
				} catch (InterruptedException ex) {}
			}
			
			//Save current time
			Difference 	= System.currentTimeMillis() - Current;
			Current 	= System.currentTimeMillis();

			//Trim states
			for (int i = 0; i < m_RemovalDepth; i++) removeState();
			m_RemovalDepth = 0;

			//Top state exist?
			boolean TopExist	= false;
			boolean ActiveFound = false;
			if (!m_StateList.isEmpty()) TopExist = m_StateList.lastElement() != null;

			//While not empty
			while (!m_StateList.isEmpty() && TopExist && !ActiveFound) {
				//If top state is not active, remove
				if (!((GameState)m_StateList.lastElement()).isActive()) removeState();
				//Otherwise, active state is found
				else ActiveFound = true;

				//Check top state
				TopExist = m_StateList.isEmpty() ? false : m_StateList.lastElement() != null;
			}

			//Quit if empty or last state doesn't exist
			if (m_StateList.isEmpty() || !TopExist) m_Running = false;
			else {
				GameState topState = (GameState)m_StateList.lastElement();
				//Get key state
				int Keys = getKeyStates();
				boolean holdable = topState.isKeyHoldable();

				//Disable some keys if not holdable
				if (!holdable) {
					if ((Keys & UP_PRESSED) != 0 && m_HoldUp)		Keys -= UP_PRESSED;
					if ((Keys & DOWN_PRESSED) != 0 && m_HoldDown)	Keys -= DOWN_PRESSED;
					if ((Keys & LEFT_PRESSED) != 0 && m_HoldLeft)	Keys -= LEFT_PRESSED;
					if ((Keys & RIGHT_PRESSED) != 0 && m_HoldRight)	Keys -= RIGHT_PRESSED;
					if ((Keys & FIRE_PRESSED) != 0 && m_HoldFire)	Keys -= FIRE_PRESSED;
					else if (m_HoldFire)							m_HoldFire = false;
				}

				//Update and draw top state
				if(!m_paused) {
					((GameState)m_StateList.lastElement()).update(Difference, Keys);
					((GameState)m_StateList.lastElement()).draw(getGraphics());
				}

				//Draw buffer
				flushGraphics();

				//Process keypress
				checkKey(Difference, Keys);
			}
		}
		
		//Terminate thread
		Main.Midlet.notifyDestroyed();
	}
	
	//The only instance
	private static StateManager s_Instance = null;
	
	//Data
	protected boolean		m_Running;
	protected Vector		m_StateList;
	protected int			m_RemovalDepth;
	protected boolean		m_paused;
	
	//Media
	protected SimplePlayer	m_SimplePlayer;
	protected SFXPlayer		m_SFXPlayers;
	protected SimpleTones	m_SimpleTones;
	protected boolean		m_isSoundOff;

	//Display
	protected int m_Row;
	protected int m_Column;

	//Input
	protected long		m_TimerUp;
	protected long		m_TimerDown;
	protected long		m_TimerLeft;
	protected long		m_TimerRight;
	protected boolean   m_HoldRight;
	protected boolean	m_HoldLeft;
	protected boolean	m_HoldFire;
	protected boolean	m_HoldDown;
	protected boolean	m_HoldUp;
	
	//Images
	protected Image m_Dark;
	protected Image m_Developer;
	protected Image m_Background;
}
